package cn.javabb.log.aspect;

import cn.javabb.common.utils.RequestUtil;
import cn.javabb.common.utils.SpringContextUtil;
import cn.javabb.core.entity.sys.LogDO;
import cn.javabb.core.service.sys.LogService;
import cn.javabb.core.util.ShiroUtil;
import cn.javabb.log.action.base.BaseActionMap;
import cn.javabb.log.action.base.ResetLog;
import cn.javabb.log.action.model.ActionModel;
import cn.javabb.log.action.model.BizMethod;
import cn.javabb.log.action.model.BizType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Method;

/**
 * @desc:
 * @author: javabb (javabob(a)163.com)
 * @create: 2019/10/27 20:35
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    private final static String DEFAULT_ACTION_NAME = "default";

    @Pointcut("@annotation(cn.javabb.log.annotation.Log)")
    public void logPointCut() {};


    @Around("logPointCut()")
    public Object recordLog(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 先执行切入点，获取返回值
        Object proceed = point.proceed();
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        /* 读取Log注解消息 */
        Method targetMethod = ((MethodSignature)(point.getSignature())).getMethod();
        cn.javabb.log.annotation.Log anno =
                targetMethod.getAnnotation(cn.javabb.log.annotation.Log.class);
        // 获取name值
        String name = anno.name();
        // 获取message值
        String message = anno.message();
        // 获取key值
        String key = anno.key();
        // 获取行为模型
        Class<? extends BaseActionMap> action = anno.action();
        BaseActionMap instance = action.newInstance();
        Object actionModel = instance.get(!key.isEmpty() ? key : DEFAULT_ACTION_NAME);
        Assert.notNull(actionModel, "无法获取日志的行为方法，请检查："+point.getSignature());
        // 封装日志实例对象
        LogDO logDO = new LogDO();
        logDO.setIp(RequestUtil.getIp());
        //logDO.setClazz(point.getTarget().getClass().getName());
        logDO.setMethod(point.getTarget().getClass().getName()+"."+targetMethod.getName()+"()");
        logDO.setType(((ActionModel) actionModel).getType());
        logDO.setName(!name.isEmpty() ? name : ((ActionModel) actionModel).getName());
        logDO.setMessage(message);
        logDO.setUseTime((int)time);
        if(ShiroUtil.getLoginUser() != null){
            logDO.setOperName(ShiroUtil.getLoginUser().getUserName());
            logDO.setOperBy(ShiroUtil.getLoginUser().getId());
        }

        //判断是否为普通实例对象
        if(actionModel instanceof BizType){
            logDO.setMessage(((BizType) actionModel).getMessage());
        }else {
            // 重置日志-自定义日志数据
            ResetLog resetLog = new ResetLog();
            resetLog.setLog(logDO);
            resetLog.setRetValue(proceed);
            resetLog.setJoinPoint(point);
            try {
                Method method = action.getDeclaredMethod(((BizMethod)actionModel).getMethod(), ResetLog.class);
                method.invoke(instance, resetLog);
                if(!resetLog.getRecord()) {
                    return proceed;
                }
            } catch (NoSuchMethodException e) {
                log.error("获取行为对象方法错误！请检查方法名称是否正确！", e);
                e.printStackTrace();
            }
        }


        // 保存日志
        LogService logService = SpringContextUtil.getBean(LogService.class);
        logService.insert(logDO);

        return proceed;
    }
}
